using System;
using System.Collections.Generic;
using System.Text;
using LibGreen.Jabber.ProtocolElements;
using LibGreen.Jabber.ProtocolElements.IqElements;
using LibGreen.Jabber.Connection;
using LibGreen.Jabber.Enumerations;

namespace LibGreen.Jabber.Dispatchers
{
    /// <summary>
    /// Processes and dispatches roster stanzas
    /// </summary>
    class RosterDispatcher : IDispatcher
    {
        private readonly Session session;
        private bool receivedRoster;

        /// <summary>
        /// Initializes a new RosterDispatcher
        /// </summary>
        /// <param name="session">The <see cref="Session"/> used by the dispatcher</param>
        public RosterDispatcher(Session session)
        {
            this.session = session;
        }

        #region IDispatcher Members
        /// <summary>
        /// Initializes the dispatcher
        /// </summary>
        public void Initialize()
        {
            session.ServerConnection.SendXmppElement(IqRosterElement.Create(session.JID));
        }

        /// <summary>
        /// Dispatches a newly received <see cref="Element"/> for processing
        /// </summary>
        /// <param name="element">The newly received <see cref="Element"/> that must be processed</param>
        /// <returns><c>true</c> if the dispatcher processed the element, <c>false</c> otherwise</returns>
        public bool DispatchElement(Element element)
        {
            if (element.Name == "iq" && element.Children.Count > 0)
            {
                string xmlns = "";
                if (element.Children[0].Attributes.TryGetValue("xmlns", out xmlns))
                {
                    if (xmlns == "jabber:iq:roster")
                    {
                        return ProcessRosterElement(new IqElement(element), element.Children[0]);
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Processes an &lt;iq&gt; element containing a roster query
        /// </summary>
        /// <param name="iqElement">The received <see cref="IqElement"/></param>
        /// <param name="queryElement">The &lt;query&gt; child of the IQ element</param>
        /// <returns><c>true</c> if the element was processed, <c>false</c> otherwise</returns>
        private bool ProcessRosterElement(IqElement iqElement, Element queryElement)
        {
            if (iqElement.OriginatingElement != null)
            {
                IqElement parent = new IqElement(iqElement.OriginatingElement);
                switch (parent.Type)
                {
                    case IqType.Get:
                        // This is the result of a roster request
                        foreach (Element child in queryElement.Children)
                        {
                            if (child.Name == "item")
                            {
                                ProcessRosterItem(child);
                            }
                        }

                        if (!receivedRoster)
                        {
                            receivedRoster = true;
                            session.Roster.OnRosterFinished();
                            // Initialize a dispatcher for <presence> notifications
                            session.ServerConnection.AddDispatcher(new PresenceDispatcher(session.ServerConnection));
                            session.ServerConnection.Parent.Presence.SendInitialPresence();
                        }
                        return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Processes an &lt;item&gt; element received from a roster update
        /// </summary>
        /// <param name="item">An <see cref="Element"/> containing item information</param>
        private void ProcessRosterItem(Element item)
        {
            string name = "", subscription = "";
            item.Attributes.TryGetValue("name", out name);
            item.Attributes.TryGetValue("subscription", out subscription);

            RosterItem rosterItem = session.Roster.FindItem(new Jid(item.Attributes["jid"]));
            rosterItem.Name = name;
            rosterItem.Subscription = SubscriptionConverter.StringToSubscription(subscription);
            if (item.Children.Count > 0)
            {
                foreach (Element group in item.Children)
                {
                    RosterGroup rosterGroup = session.Roster.FindGroup(group.Value);
                    rosterItem.AddGroup(rosterGroup);
                    rosterGroup.AddItem(rosterItem);
                }
            }
            else
            {
                session.Roster.NullGroup.AddItem(rosterItem);
            }
        }

        #endregion
    }
}
